// Schedule Sorter Algorithm
// Coded by: Darren and Bryan
#include "schedulesorter.h"
#include "student.h"
#include "registrar.h"

using namespace std;

bool findConflict(vector<int> time1, vector<int> time2)
{
    if(time1.size() == 0 || time2.size() == 0){
        return false;
    }
    
    int s1 = time1[0];
    int e1 = time1[1];
    int s2 = time2[0];
    int e2 = time2[1];

    if((s2 > s1 && s2 < e1) || (s1 > s2 && s1 < e2)){
        return true;
    }
    else{
        return false;
    }
}

bool courseSecCompare(vector<CourseSection*> seq1, vector<CourseSection*> seq2)
{
    vector<CourseSection*>::iterator ref, var;
    for(ref = seq1.begin(); ref != seq1.end(); ++ref){
        for(var = seq2.begin(); var != seq2.end(); ++var){
            if(findConflict((*ref)->getTime(1), (*var)->getTime(1))){
                return true;
            }
            if(findConflict((*ref)->getTime(1), (*var)->getTime(2))){
                return true;
            }
            if(findConflict((*ref)->getTime(2), (*var)->getTime(1))){
                return true;
            }
            if(findConflict((*ref)->getTime(2), (*var)->getTime(2))){
                return true;
            }
        }
    }

    return false;
}

bool slotConflict(vector<CourseSection*> slot)
{
    vector<CourseSection*>::iterator ref, var;

    for(ref = slot.begin(); ref != slot.begin() + slot.size() - 1; ++ref){
        vector<CourseSection*> seqTemp1;
        
        seqTemp1.insert(seqTemp1.end(), *ref);
        boost::weak_ptr<CourseSection> r = (*ref)->getParent();
        while(r.use_count() != 0){
            seqTemp1.insert(seqTemp1.begin(), r.lock().get());
            r = (*ref)->getParent();
        }

        for(var = ref + 1; var != slot.end(); ++var){
            vector<CourseSection*> seqTemp2;

            seqTemp2.insert(seqTemp2.end(), *var);
            boost::weak_ptr<CourseSection> v = (*var)->getParent();
            while(v.use_count() != 0){
                seqTemp2.insert(seqTemp1.begin(), v.lock().get());
                v = (*var)->getParent();
            }

            if(courseSecCompare(seqTemp1, seqTemp2)){
                return true;
            }
        }
    }
    return false;
}

void leafRecursion(CourseSection* sectionRef, vector<CourseSection*>* leafListRef)
{
    using boost::shared_ptr;
    if(sectionRef->getSubsections().empty() ){ // base case (leaf node)
        leafListRef->push_back(sectionRef);
    }
    else{
        vector<shared_ptr<CourseSection> >::iterator cs;
        vector<shared_ptr<CourseSection> > subsecs(sectionRef->getSubsections());
        for(cs = subsecs.begin(); cs != subsecs.end(); ++cs){
            leafRecursion(cs->get(), leafListRef);
        }
    }
}

void getSectionsFromPointers(vector<CourseSection*> sectionPointers, vector<CourseSection>* sections){
    vector<CourseSection*>::iterator p;
    for(p = sectionPointers.begin(); p != sectionPointers.end(); ++p){
        sections->push_back(**p);
    }
}

void slotFunction(vector<vector<CourseSection*> >::iterator iBegin, vector<vector<CourseSection*> >::iterator iEnd, vector<vector<CourseSection*> >::iterator iCurrent, vector<CourseSection*> tempSlot, vector<Schedule> &schedules){
    using boost::shared_ptr;
    
    vector<CourseSection*> currentCourse = *iCurrent;
    vector<CourseSection*>::iterator cs;

    if(iCurrent == iEnd){ // base case
        for(cs = currentCourse.begin(); cs != currentCourse.end(); ++cs){
            tempSlot.push_back(*cs);
            if(!slotConflict(tempSlot)){
                vector<CourseSection> tempSections;
                getSectionsFromPointers(tempSlot, &tempSections);
                
                // hold student-set constraints
                const Student student(Registrar::instance()->student());
                shared_ptr<Constraint> constraints(student.getConstraints());
                if( constraints->consider(tempSections) )
                    schedules.push_back(tempSections);
            }
            tempSlot.pop_back();
        }
    }
    else{
        for(cs = currentCourse.begin(); cs != currentCourse.end(); ++cs){
            tempSlot.push_back(*cs);
            slotFunction(iBegin, iEnd, iCurrent+1, tempSlot, schedules);
            tempSlot.pop_back();
        }
    }
}

void scheduleSorter(vector<Course*> courses, vector<Schedule> &validSchedules, int sem)
{
    vector<Course*>::iterator c;
    vector< boost::shared_ptr<CourseSection> >::iterator l;
    vector<vector<CourseSection*> > leafList;

    for(c = courses.begin(); c != courses.end(); ++c){
        vector<boost::shared_ptr<CourseSection> > lectures = (*c)->getLectures();
        vector<CourseSection*> courseTemp;
        for(l = lectures.begin(); l != lectures.end(); ++l){            
            if((*l)->getSemester() == sem){
                leafRecursion((*l).get(), &courseTemp);
            }
        }
        leafList.insert(leafList.end(), courseTemp);
    }

    vector<CourseSection*> temp;
    slotFunction(leafList.begin(), leafList.end(), leafList.begin(), temp, validSchedules);
}
